home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1997 January: Mac OS SDK / Dev.CD Jan 97 SDK2.toast / Development Kits (Disc 2) / OpenDoc / OpenDoc Development / Debugging Support / OpenDoc™ Source Code / Storage / Bento / CM / TOCEnts.c < prev    next >
Encoding:
Text File  |  1996-08-28  |  84.2 KB  |  1,790 lines  |  [TEXT/MPS ]

  1. /*
  2.     File:         TOCEnts.c  
  3.  
  4.     Contains:    Container Manager TOC Entry Routines
  5.  
  6.     Written by:    Ira L. Ruben
  7.  
  8.     Owned by:    Ed Lai
  9.  
  10.     Copyright:    © 1991 - 1996 by Apple Computer, Inc., all rights reserved.
  11.  
  12.     Change History (most recent first):
  13.  
  14.          <3>     8/13/96    DM        1362809: disable containers on error
  15.          <3>     7/28/95    EL        #1272079: Value should not use the continue
  16.                                     flag until being written to TOC.
  17.          <2>     8/26/94    EL        #1181622 Ownership update.
  18.          <3>      6/3/94    EL        Rename Values.h/c to ValueRtn.h/c.
  19.          <2>     3/31/94    EL        Export cmFreeProperties. #1150214
  20.          <1>      2/3/94    EL        first checked in
  21.          <4>      2/2/94    EL        Fix memory leak by freeing property and
  22.                                                     value associated with a reference.
  23.          <3>    10/29/93    EL        Add deleteAssocatedRef call which would
  24.                                                     delete a reference associated with a value
  25.                                                     without leaving it on the delete list
  26.          <2>    10/21/93    EL        ValueHdr should not point to deleted
  27.                                                     property
  28.     To Do:
  29. */
  30.  
  31. /*---------------------------------------------------------------------------*
  32.  |                                                                           |
  33.  |                            <<<  TOCEnts.c   >>>                           |
  34.  |                                                                           |
  35.  |                    Container Manager TOC Entry Routines                   |
  36.  |                                                                           |
  37.  |                               Ira L. Ruben                                |
  38.  |                                 12/02/91                                  |
  39.  |                                                                           |
  40.  |                  Copyright Apple Computer, Inc. 1991-1992                 |
  41.  |                           All rights reserved.                            |
  42.  |                                                                           |
  43.  *---------------------------------------------------------------------------*
  44.  
  45.  This file contains the manipulation routines to handle TOC objects below the object
  46.  level.  The objects and the means to get at them are maintained in TOCObjects.c.  The
  47.  split is done this way to keep the accessing of objects separate from the stuff 
  48.  represented by those objects.
  49.  
  50.  The following diagram shows the pointer relationships among the various data structures
  51.  descending from an object.  The data structurs leading to the object itself are handled
  52.  independently by the  TOCObjs.c   routines.  They are of no concern here as the structures
  53.  described here are of no concern to  TOCObjs.c  !
  54.  
  55.  Arrows of the form "<---->" represent double links of forward/backward link lists. Arrows
  56.  of the form "--->" are pointers in the indicated direction.  A "+" in an arrow just means
  57.  it's turning or intersecting since there is no other means to notate it given the
  58.  character set used!
  59.  
  60.  A name above a box corresponds to the actual typedef struct name.  A "*name*" just
  61.  signifies what the structure is and destinguishes it from other similar structures in the
  62.  diagram.  Names in brackets are fields in the structures.  The fields representing the
  63.  list links and headers are not shown.
  64.  
  65.  
  66.                         +<-----+<--------------------+<--------------------+
  67.                         |      |                     |                     |
  68.       TOCObject         |      | TOCProperty         |                     |
  69.  *------------------*<--+     *--------------*      *--------------*      *--------------*
  70.  |     *Object*     |<------->|  *Property1* |<---->|  *Property2* |<---->|  *Property3* |
  71.  |   [objectID]     | +>+---->| [propertyID] |      | [propertyID] |      | [propertyID] |
  72.  |   [container]    | | |     *--------------*      *--------------*<-+   *--------------*<-+
  73.  |   [nextObject]   | | |                                             |                     |
  74.  |[nextTypeProperty]| | |                                             |                     |
  75.  |  [objectFlags]   | | |                                             +->...                +->...
  76.  *------------------* | |
  77.                       | |       TOCValueHdr          TOCValue
  78.                       | |    *--------------*      *----------*
  79.                       | +--->|  *ValueHdr1* |<---->| *Value1* |
  80.  The nextObject       +<-----|   [typeID]   |      | [flags]  |
  81.  links ALL objects.   |      | [container]  |      | [value]  |
  82.                       |      | [valueFlags] |      *----------*
  83.                       |      | [generation] |           |
  84.                       | +--->|    [size]    |           |
  85.  The nextTypeProperty | |    *--------------*<----------+
  86.  is used for two      | |
  87.  separate chains;     | |
  88.  one that links all   | |
  89.  property objects,    | |    *--------------*      *-----------*      *-----------*
  90.  and one that links   | +--->|  *ValueHdr2* |<---->| *Value21* |<---->| *Value22* |
  91.  all type objects.    +<-----|   [typeID]   |      |  [flags]  |      |  [flags]  |
  92.                       |      | [container]  |      |  [value]  |      |  [value]  |
  93.                       |      | [valueFlags] |      *-----------*      *-----------*
  94.                       |      | [generation] |            |                  |
  95.                       | +--->|    [size]    |            |                  |
  96.                       | |    *--------------*<-----------+<-----------------+   
  97.                       | |                                     (continued)
  98.                       | |
  99.                       | |
  100.                       | |
  101.                       | |    *--------------*      *-----------*    Note, all value
  102.                       | +--->|  *ValueHdr3* |<---->|  *Value3* |    entries have a ptr
  103.                       +<-----|   [typeID]   |      |  [flags]  |    to their "owning"
  104.                              | [container]  |      |  [value]  |    ValueHdr to be able
  105.                              | [valueFlags] |      *-----------*    to get at the TypeID
  106.                              | [generation] |            |          and container.
  107.                              |    [size]    |            |
  108.                              *--------------*<-----------+
  109.  
  110.  
  111.  The above diagram shows a single object with three properties. Properties are chained off
  112.  the object. Only the layout for the first property is shown.  Propert1 has three values.
  113.  Values are chained off a value header and the value headers are chained off the properties.
  114.  Value2 represents a continued value. They are chained together, and each value entry
  115.  (continued or not) has a back pointer to its value header.  Similarly, the value headers
  116.  have back pointers to their property and the properties back to their object.  This
  117.  allows us to get at the owning container and list headers.
  118.  
  119.  Not shown in this example diagram is the handling of global names. They are contained in
  120.  a binary tree symbol table.  Each entry in that symbol table contains the symbol itself
  121.  and a back pointer to its corresponding value entry for which the global name represents
  122.  the value.  The value, in turn, points to its global name symbol table entry.  This
  123.  global name layout allows us to keep the names in memory for mapping of global names
  124.  to objects or vice versa.  By keeping the global names on a binary tree we can
  125.  efficiently walk the tree to write the value out to the container. This is more efficient
  126.  than the alternative, which is walking the entire TOC structure looking only for the
  127.  global name value pointers to just write them out.
  128.  
  129.  Note, the API permits deletion of objecs and values.  This is implemented by two separate
  130.  deletion lists.  There is a list of all deleted TOCObject's and another list of all
  131.  deleted TOCValueHdr's.  Pointers to these are returned to the API user as "refNums". Thus
  132.  these are the only entities that we must never free in an open container.  All the other
  133.  stuff, including TOCValue's off the TOCValueHdr's, can be freed when a delete is done.
  134.  The reason we keep this stuff around at all is to protect ourselves against the silly
  135.  user passing a "refNum" to a deleted object back to us!  The "refNum"s remain valid
  136.  pointers.  When a TOCObject or TOCValueHdr is put on its list, it is flagged as deleted.
  137.  Wenever a API caller passes in a "refNum" we check it to make sure it's still valid and
  138.  yell if it isn't.  It's a lot of machinery for this.  But do you have a better idea?
  139.  
  140.  
  141.             ---> Help for those looking at this file for the firtst time <---
  142.  
  143.  The place to start looking is at the four routines cmAppendValue(), defineValue(),
  144.  defineProperty(), and cmDefineObject().  They are the main routines that implement the
  145.  above structures. Understand them and the rest should fall out.  That's how this file was
  146.  developed and all other stuff "spun out" from there.
  147. */
  148.  
  149.  
  150. #include <stddef.h>
  151. #include <string.h>
  152. #include <setjmp.h>
  153. #include <stdio.h>
  154.  
  155. #ifndef __CMTYPES__
  156. #include "CMTypes.h"
  157. #endif
  158. #ifndef __CM_API__
  159. #include "CMAPI.h"
  160. #endif
  161. #ifndef __LISTMGR__
  162. #include "ListMgr.h"
  163. #endif
  164. #ifndef __TOCENTRIES__
  165. #include "TOCEnts.h"   
  166. #endif
  167. #ifndef __TOCOBJECTS__
  168. #include "TOCObjs.h"   
  169. #endif
  170. #ifndef __TOCIO__
  171. #include "TOCIO.h"
  172. #endif
  173. #ifndef __GLOBALNAMES__
  174. #include "GlbNames.h"   
  175. #endif
  176. #ifndef __CONTAINEROPS__
  177. #include "Containr.h"  
  178. #endif
  179. #ifndef __VALUEROUTINES__
  180. #include "ValueRtn.h"       
  181. #endif
  182. #ifndef __HANDLERS__
  183. #include "Handlers.h"
  184. #endif
  185. #ifndef __UPDATING__
  186. #include "Update.h"  
  187. #endif
  188. #ifndef __DYNAMICVALUES__
  189. #include "DynValus.h"     
  190. #endif
  191. #ifndef __BUFFEREDIO__
  192. #include "BufferIO.h"  
  193. #endif
  194. #ifndef __REFERENCES__
  195. #include "Refs.h"      
  196. #endif
  197. #ifndef __FREESPACE__
  198. #include "FreeSpce.h" 
  199. #endif
  200. #ifndef __SESSIONDATA__
  201. #include "Session.h"          
  202. #endif
  203. #ifndef __ERRORRPT__
  204. #include "ErrorRpt.h"      
  205. #endif
  206. #ifndef __UTILITYROUTINES__
  207. #include "Utility.h"        
  208. #endif
  209.  
  210.                                                                     CM_CFUNCTIONS
  211.  
  212. /* The following generates a segment directive for Mac only due to 32K Jump Table             */
  213. /* Limitations.  If you don't know what I'm talking about don't worry about it.  The        */
  214. /* default is not to generate the pragma.  Theoritically unknown pragmas in ANSI won't    */
  215. /* choke compilers that don't recognize them.  Besides why would they be looked at if        */
  216. /* it's being conditionally skipped over anyway?  I know, famous last words!                        */
  217.  
  218. #if CM_MPW
  219. #pragma segment TOCEntries
  220. #endif
  221.  
  222.  
  223. #ifndef DEBUG_TOCENTRIES
  224. #define DEBUG_TOCENTRIES 0
  225. #endif
  226.  
  227. #define dbgFile stdout                    /* trace display output file                                                        */
  228.  
  229. /* The following is used in this file by cmWalkThroughEntireTOC(), etc. to hold the         */
  230. /* function pointers, caller's refCon, and setjmp/longjmp escape vector.  See that             */
  231. /* routine for further details.                                                                                                                    */
  232.  
  233. struct TOCActions {                            /* cmWalkThroughEntireTOC() communication area layout:    */
  234.         jmp_buf walkThroughEnv;            /*         MUST be 1st for AbortWalkThroughEntireTOC() macro */
  235.         CMRefCon refCon;                        /*         caller's refCon                                                                         */
  236.         TOCWalkReturns (*objectAction)(ContainerPtr container, TOCObjectPtr theObject, CMRefCon refCon);
  237.         TOCWalkReturns (*propertyAction)(ContainerPtr container, TOCPropertyPtr theProperty, CMRefCon refCon);
  238.         TOCWalkReturns (*valueHdrAction)(ContainerPtr container, TOCValueHdrPtr theValueHdr, CMRefCon refCon);
  239.         TOCWalkReturns (*valueAction)(ContainerPtr container, TOCValuePtr theValue, CMRefCon refCon);
  240. };
  241. typedef struct TOCActions TOCActions, *TOCActionsPtr;
  242.  
  243. static void deleteProperties(TOCObjectPtr theObject, CMRefCon refCon);
  244.  
  245. /*---------------------------------------------------------------------------*
  246.  | deleteAssociatedRef - remove the reference associated with this reference |
  247.  *---------------------------------------------------------------------------*
  248.  
  249.  This routine checks to see if there is any reference assoicated with a value, if there
  250.  is one, we also delete the reference.
  251.  
  252.  Since a reference is delete as a side effect, if the delete reference remains in the
  253.  delete list and generate a update delete, then when the update is play back, the
  254.  reference is deleted twice, once as the side effect and once as directed by the updating
  255.  instruction. So we want to make sure the reference is completely gone so it would not
  256.  be used to generate a delete update later.
  257. */
  258.  
  259. static void CM_NEAR deleteAssociatedRef(ContainerPtr container, TOCValueHdrPtr theValueHdr)
  260. {
  261.     TOCObjectPtr    theObject;
  262.     void                     *toc;
  263.     CMBoolean                orgSetting;
  264.     
  265.     if (HasRefDataObject(theValueHdr))    {                                                /* delete refs object...    */
  266.         theObject = RefDataObject(theValueHdr);
  267.         toc = container->toc;
  268.         theObject->objectFlags &= ~ProtectedObject;
  269.         cmUnlinkObject(toc, theObject);                                                        /* must do this first            */
  270.         orgSetting = cmKeepDeletedRefNums(toc);
  271.         /* since the reference is deleted as a side effect, we don't want it to show up in     */
  272.         /* any delete list or touch list, so we going to free it directly, and not put it     */
  273.         /* on the delete or touch list. We do this by setting RefNumHandling to false.            */
  274.         cmChangeRefNumHandling(toc, false);                                                /* allows freeing                    */
  275.         cmDelete1Object(toc, theObject->objectID, (void *)container, deleteProperties);
  276.         cmChangeRefNumHandling(toc, orgSetting);                                    /* restore original    value */
  277.     }
  278.     
  279.     #if CMSHADOW_LIST
  280.     if (HasRefShadowList(theValueHdr))                                                    /* delete refs shadow list*/
  281.         cmDeleteRefDataShadowList(theValueHdr);
  282.     #endif
  283. }
  284.  
  285. /*--------------------------------------------------------------*
  286.  | cmMarkValueDeleted - remove a value for an object's property |
  287.  *--------------------------------------------------------------*
  288.  
  289.  This routine takes a pointer to a value header in the specified container and deletes it.
  290.  If the value was the only one for its property, the property is also deleted.
  291.  
  292.  Note, properties and values actually have their memory freed up.  The value headers are
  293.  placed on the deletedValues list to keep them around, but out of the TOC.  This is to
  294.  prevent dangling references from API user "refNum"s, because it is value header pointers
  295.  that we give to the user as those "refNum"s.  Such value headers are marked as "deleted"
  296.  to prevent future use by the user.
  297.  
  298.  The moving of the deleted value headers to the deletedValues list can be suppressed by 
  299.  passing the deleteHdr parameter as true.  Its memory is simply freed along with the rest.
  300.  This is used currently in only one place for error recovery when dynamic value setup
  301.  fails.  There we want to free all the dynamic value layers (which are headers), and
  302.  finally the base "real" value itself.  It is that final value that is passed to here to
  303.  get rid of.
  304. */
  305.  
  306. void cmMarkValueDeleted(ContainerPtr container, TOCValueHdrPtr theValueHdr, 
  307.                                                 CMBoolean deleteHdr)
  308. {
  309.     TOCValuePtr         nextValue, theValue;
  310.     TOCPropertyPtr theProperty;
  311.     
  312.     /* Free all values for the value header...                                                                                        */
  313.     
  314.     theValue = (TOCValuePtr)cmGetListHead(&theValueHdr->valueList);
  315.     while (theValue) {
  316.         nextValue = (TOCValuePtr)cmGetNextListCell(theValue);
  317.         cmAddToFreeList(container, theValue, 0, 0);                        /* add space to free list            */
  318.         if (theValue->flags & kCMGlobalName)                                    /* mark global name as deleted*/
  319.             if (theValue->value.globalName.globalNameSymbol) 
  320.                 MarkGlobalNameDeleted(theValue->value.globalName.globalNameSymbol);
  321.         CMfree(theValue);
  322.         theValue = nextValue;
  323.     } /* value */
  324.     
  325.     /* Mark the value header as deleted and put it on the separate chain of deleted             */
  326.     /* values.  Also keep track of total space deleted.                                                                        */
  327.     
  328.     #if 0 /* now done in cmAddToFreeList() */
  329.     container->spaceDeletedValue->value.imm.ulongValue += theValueHdr->size;
  330.     #endif
  331.     
  332.     theProperty = theValueHdr->theProperty;                                            /* prepare to remove hdr    */
  333.     cmDeleteListCell(&theProperty->valueHdrList, theValueHdr);    /* del. valueHdr off list    */
  334.     
  335.     deleteAssociatedRef(container, theValueHdr);                                /* delete any reference        */
  336.     
  337.     if (deleteHdr || !cmKeepDeletedRefNums(container->toc)) {        /* free the refNum?                */    
  338.         CMfree(theValueHdr);                                                                            /* this caller is serious!*/
  339.         theValueHdr = NULL;                                                                                /* so we know it's gone     */
  340.     } else {                                                                                                            /* this is normal case...    */
  341.         cmInitList(&theValueHdr->valueList);                                            /* no more values                    */
  342.         theValueHdr->valueFlags |= ValueDeleted;                                    /* mark hdr as deleted        */
  343.         cmAppendListCell(&container->deletedValues, theValueHdr);    /* move to deletedValues     */
  344.     }
  345.     
  346.     /* If the value is the only one for a property, the property itself is deleted...            */
  347.     
  348.     if (cmIsEmptyList(&theProperty->valueHdrList)) {
  349.         CMfree(cmDeleteListCell(&theProperty->theObject->propertyList, theProperty));
  350.         if (theValueHdr)
  351.             theValueHdr->theProperty = NULL;                                                /* so we won't access it     */
  352.     }
  353. }
  354.  
  355.  
  356. /*-----------------------------------------------------------------------*
  357.  | cmDeleteProperty - remove a property and all its values for an object |
  358.  *-----------------------------------------------------------------------*
  359.  
  360.  This routine takes a pointer to a property in the specified container and deletes it and
  361.  all its values.
  362.  
  363.  Note, properties and values actually have their memory freed up.  The value headers are
  364.  placed on the deletedValues list to keep them around, but out of the TOC.  This is to
  365.  prevent dangling references from API user "refNum"s, because it is value header pointers
  366.  that we give to the user as those "refNum"s.  Such value headers are marked as "deleted"
  367.  to prevent future use by the user.
  368.  
  369.  This routine differs from deleteProperties() below by the fact that this is an external
  370.  routine for deleting one property for an object. It is used as an isolated and explicit 
  371.  delete of a property.  deleteProperties(), on the other hand, is for deleting all the
  372.  properties of an object when we know we're deleting the object itself.  It is the action
  373.  routine for the isolated delete of an object.
  374. */
  375.  
  376. void cmDeleteProperty(ContainerPtr container, TOCPropertyPtr theProperty)
  377. {
  378.     TOCValueHdrPtr theValueHdr, nextValueHdr;
  379.     TOCValuePtr         theValue, nextValue;
  380.     TOCObjectPtr      theObject;
  381.     
  382.     /* Mark all the values for the property as deleted...                                                                    */
  383.     
  384.     theObject     = theProperty->theObject;
  385.     theValueHdr = (TOCValueHdrPtr)cmGetListHead(&theProperty->valueHdrList);
  386.     
  387.     while (theValueHdr) {
  388.         nextValueHdr = (TOCValueHdrPtr)cmGetNextListCell(theValueHdr);
  389.         
  390.         theValue = (TOCValuePtr)cmGetListHead(&theValueHdr->valueList);
  391.         while (theValue) {
  392.             nextValue = (TOCValuePtr)cmGetNextListCell(theValue);
  393.             cmAddToFreeList(container, theValue, 0, 0);                    /* add space to free list            */
  394.             if (theValue->flags & kCMGlobalName)                                /* mark global name as deleted*/
  395.                 if (theValue->value.globalName.globalNameSymbol) 
  396.                     MarkGlobalNameDeleted(theValue->value.globalName.globalNameSymbol);
  397.             CMfree(theValue);
  398.             theValue = nextValue;
  399.         } /* value */
  400.         
  401.         #if 0 /* now done in cmAddToFreeList() */
  402.         container->spaceDeletedValue->value.imm.ulongValue += theValueHdr->size;
  403.         #endif
  404.         
  405.         deleteAssociatedRef(container, theValueHdr);                            /* delete any reference        */
  406.         
  407.         if (cmKeepDeletedRefNums(container->toc)) {                             /* keep refNums ?                     */
  408.             cmInitList(&theValueHdr->valueList);                                        /* no more values                    */
  409.             theValueHdr->valueFlags |= ValueDeleted;                                /* mark valueHdr deleted    */
  410.             cmAppendListCell(&container->deletedValues,theValueHdr);/* move to deletedValues     */
  411.             cmImplicitDeleteValueTouch(theValueHdr, theObject);            /* check touched updates    */
  412.         } else                                                                                                        /* don't keep refNums            */
  413.             CMfree(theValueHdr);
  414.         
  415.         theValueHdr = nextValueHdr;                                                        /* around and around we go...    */
  416.     } /* valueHdr */
  417.     
  418.     /* Free the property itself since it now has no values...                                                            */
  419.     
  420.     CMfree(cmDeleteListCell(&theObject->propertyList, theProperty));
  421. }
  422.  
  423.  
  424. /*---------------------------------------------------------------------------------*
  425.  | deleteProperties - delete ALL the properties and and their values for an object |
  426.  *---------------------------------------------------------------------------------*
  427.  
  428.  This routine is an action routine for cmDelete1Object() initiated by cmMarkObjectDeleted()
  429.  below to free up all the memory for values and properties for an object.  The value
  430.  headers are marked as deleted and move to the deletedValues list.  This makes sure there
  431.  are no dangling value "refCon"s (which are value header pointers) laying around.  Such
  432.  value headers are flagged as deleted to prevent their use (assuming the checks are put
  433.  in, of course).
  434.  
  435.  Note, this routine differs from cmDeleteProperty() in that here we delete ALL the
  436.  properties for an object.  Not just a single property.  Also, unlike cmDeleteProperty(),
  437.  we know we are being called because an object is being deleted.  We thus do no try to
  438.  delete the object because all its properties are deleted as cmDeleteProperty() does.
  439.  
  440.  Also note, this "static" is intentionally left to default memory model under DOS since it
  441.  is passed as a function pointer to cmDelete1Object().
  442. */
  443.  
  444. static void deleteProperties(TOCObjectPtr theObject, CMRefCon refCon)
  445. {
  446.     ContainerPtr      container = (ContainerPtr)refCon;
  447.     TOCPropertyPtr theProperty, nextProperty;
  448.     TOCValueHdrPtr theValueHdr, nextValueHdr;
  449.     TOCValuePtr         theValue, nextValue;
  450.     
  451.     /* Free all the properties for this object...                                                                                    */
  452.     
  453.     theProperty = (TOCPropertyPtr)cmGetListHead(&theObject->propertyList);
  454.     while (theProperty) {
  455.         nextProperty = (TOCPropertyPtr)cmGetNextListCell(theProperty);
  456.     
  457.         /* Mark all the value headers for the property as deleted and place them on a             */
  458.         /* separate list.  The values themselves are freed.                                                                    */
  459.         
  460.         theValueHdr = (TOCValueHdrPtr)cmGetListHead(&theProperty->valueHdrList);
  461.         while (theValueHdr) {
  462.             nextValueHdr = (TOCValueHdrPtr)cmGetNextListCell(theValueHdr);
  463.             
  464.             theValue = (TOCValuePtr)cmGetListHead(&theValueHdr->valueList);
  465.             while (theValue) {
  466.                 nextValue = (TOCValuePtr)cmGetNextListCell(theValue);
  467.                 cmAddToFreeList(container, theValue, 0, 0);                /* add space to free list            */
  468.                 if (theValue->flags & kCMGlobalName)                            /* mark global name as deleted*/
  469.                     if (theValue->value.globalName.globalNameSymbol) 
  470.                         MarkGlobalNameDeleted(theValue->value.globalName.globalNameSymbol);
  471.                 CMfree(theValue);
  472.                 theValue = nextValue;
  473.             } /* value */
  474.             
  475.             /*CMfree(theValueHdr);*/                                                        /* no longer freed                        */
  476.             
  477.             #if 0 /* now done in cmAddToFreeList() */
  478.             container->spaceDeletedValue->value.imm.ulongValue += theValueHdr->size;
  479.             #endif
  480.             
  481.             deleteAssociatedRef(container, theValueHdr);                            /* delete any reference    */
  482.             
  483.             if (cmKeepDeletedRefNums(container->toc)) {                                /* keep refNums ?                */
  484.                 cmInitList(&theValueHdr->valueList);                                        /* no more values                */
  485.                 theValueHdr->valueFlags |= ValueDeleted;                                /* mark valueHdr deleted*/
  486.                 cmAppendListCell(&container->deletedValues,theValueHdr);/* move to deletedValues*/
  487.                 cmImplicitDeleteValueTouch(theValueHdr, theObject);            /* check touched updates*/
  488.             } else                                                                                                        /* don't keep refNums        */
  489.                 CMfree(theValueHdr);
  490.                 
  491.             theValueHdr = nextValueHdr;                                                    /* around and around we go...    */
  492.         } /* valueHdr */
  493.         
  494.         CMfree(theProperty);                                                                    /* property not needed                */    
  495.         theProperty = nextProperty;
  496.     } /* property */
  497.     
  498.     cmInitList(&theObject->propertyList);                                        /* no properties for object        */
  499. }
  500.  
  501.  
  502. /*-------------------------------------------------*
  503.  | cmMarkObjectDeleted - mark an object as deleted |
  504.  *-------------------------------------------------*
  505.  
  506.  This routine is called to remove an entire object from the TOC for the specified
  507.  container.  All its associated structures (properties and values) are made unavailable
  508.  from the TOC.  The space for the object is NOT freed. The object is placed on a list of
  509.  deleted objects and marked as deleted.  By doing it this way, any outstanding "refNum"s
  510.  we gave the API user (which are pointers to this stuff) will still be "valid" (sort of
  511.  -- at least not looking at garbage).
  512.  
  513.  Moving the object structure takes all its substructures (properties, value headers, and
  514.  values with it).  But the API user may also have references to value headers!  So we have
  515.  to mark these as deleted as well (we can still, however, free the memory for the value
  516.  lists attached to the value headers and the properties which contain the value headers).
  517.  
  518.  The object deletion (and actual marking) is handled by cmDelete1Object().  It takes an
  519.  action routine to walk an object's properties.  We pass it deleteProperties() to do that.
  520.  It is there that we delete all the properties and values and move the value headers to
  521.  there deletedValues list.
  522. */
  523.  
  524. void cmMarkObjectDeleted(ContainerPtr container, TOCObjectPtr theObject)
  525. {
  526.     /* Here we use cmDelete1Object() which removes an object by object id.  It takes a        */
  527.     /* function pointer (markValuesDeleted here) to delete the stuff attached to the             */
  528.     /* object prior to deleting the object itself. Note, we must also remember to unlink    */
  529.     /* the object from the master chains.  We do this first since cmDelete1Object() will     */
  530.     /* use the same link fields to relink the object to the list of deleted objects.            */
  531.     
  532.     cmUnlinkObject(container->toc, theObject);                            /* must do this first                    */
  533.     cmDelete1Object(container->toc, theObject->objectID, (void *)container, deleteProperties);
  534. }
  535.  
  536.  
  537. /*-------------------------------------------------------------------------------*
  538.  | cmFreeProperties - free ALL the properties and and their values for an object |
  539.  *-------------------------------------------------------------------------------*
  540.  
  541.  This routine is called to unconditionally free all the properties for an
  542.  objects and the values and other data structures associated with those properties.  It is
  543.  done when an TOC is deleted, i.e, for cmFreeTOC().
  544.  
  545.  Note, here we can just free the properties and associated data structures.  We don't have
  546.  to respect the fact that they are on a list whose header belongs to the the object.  That
  547.  is because we know we are going to delete the object too.
  548.  
  549.  This routine differs from deleteProperties(), and cmDeleteProperty() in that we just free
  550.  the data.  There is no list maintenance and we do not have to worry about moving deleted
  551.  objects or value headers to their delete lists.
  552.  
  553.  Note, this "static" is intentionally left to default memory model under DOS since it is
  554.  passed as a function pointer to cmDestroyTOC().
  555. */
  556.  
  557. void cmFreeProperties(TOCObjectPtr theObject, CMRefCon refCon)
  558. {
  559.     ContainerPtr      container = (ContainerPtr)refCon;
  560.     TOCPropertyPtr theProperty, nextProperty, theDynProperty = NULL;
  561.     TOCValueHdrPtr theValueHdr, nextValueHdr;
  562.     TOCValuePtr         theValue, nextValue;
  563.     
  564.     /* Free this object's touched list (if any)...                                                                                */
  565.     
  566.     cmDeleteTouchedList(theObject, container);
  567.     
  568.     /* Free all the properties for this object...                                                                                    */
  569.     
  570.     theProperty = (TOCPropertyPtr)cmGetListHead(&theObject->propertyList);
  571.     while (theProperty) {
  572.         nextProperty = (TOCPropertyPtr)cmGetNextListCell(theProperty);
  573.         
  574.         /* Free all the values and their headers for a property. If a "real" value is for a */
  575.         /* dynamic value, the associated dynamic value layers are also freed.  This should     */
  576.         /* end up freeing ALL the dynamic values for this object, because we visit all the     */
  577.         /* real values in the object.  This will leave the dynamic value property with no        */
  578.         /* dynamic values on it.  But we have to be careful to not free the dynamic value        */
  579.         /* property until we have looked at all the values for the object.  Thus we remember*/
  580.         /* where the property is and delete it last.                                                                                */
  581.         
  582.         if (theProperty->propertyID == CM_StdObjID_DynamicValues) { /* skip dyn value prop.    */
  583.             theDynProperty = theProperty;                                                            /* but remember it            */
  584.             theProperty      = nextProperty;
  585.             continue;
  586.         }
  587.         
  588.         theValueHdr = (TOCValueHdrPtr)cmGetListHead(&theProperty->valueHdrList);
  589.         
  590.         if (theValueHdr->dynValueData.dynValue != NULL)                    /* free dynamic values...        */
  591.             cmDeleteAllDynamicValueLayers(theValueHdr->dynValueData.dynValue, false);
  592.         
  593.         while (theValueHdr) {
  594.             nextValueHdr = (TOCValueHdrPtr)cmGetNextListCell(theValueHdr);
  595.             
  596.             theValue = (TOCValuePtr)cmGetListHead(&theValueHdr->valueList);
  597.             while (theValue) {
  598.                 nextValue = (TOCValuePtr)cmGetNextListCell(theValue);
  599.                 
  600.                 #if 0                                                                                                /* table already deleted!        */
  601.                 if (theValue->flags & kCMGlobalName)                                
  602.                     if (theValue->value.globalName.globalNameSymbol) 
  603.                         MarkGlobalNameDeleted(theValue->value.globalName.globalNameSymbol);
  604.                 #endif
  605.                 
  606.                 CMfree(theValue);
  607.                 theValue = nextValue;
  608.             } /* value */
  609.  
  610.             #if CMSHADOW_LIST
  611.             if (HasRefShadowList(theValueHdr))                                        /* delete refs shadow list    */
  612.                 cmDeleteRefDataShadowList(theValueHdr);
  613.             #endif
  614.             
  615.             CMfree(theValueHdr);
  616.             theValueHdr = nextValueHdr;
  617.         } /* valueHdr */
  618.         
  619.         CMfree(theProperty);
  620.         theProperty = nextProperty;
  621.     } /* property */
  622.     
  623.     /* If there was a dynamic value property, then by this time all of its dynamic values    */
  624.     /* have been deleted.  All that's left is to remove the property from the object. But    */
  625.     /* given that we're expected to MAKE SURE all TOC memory is released we're going to be*/
  626.     /* paranoid here and formally attempt to free any value headers and their value                */
  627.     /* segments just as we did above.  There shouldn't be any, but lets just be sure.            */
  628.     
  629.     if (theDynProperty != NULL) {
  630.         theValueHdr = (TOCValueHdrPtr)cmGetListHead(&theDynProperty->valueHdrList);
  631.         while (theValueHdr) {
  632.             nextValueHdr = (TOCValueHdrPtr)cmGetNextListCell(theValueHdr);
  633.             theValue = (TOCValuePtr)cmGetListHead(&theValueHdr->valueList);
  634.             while (theValue) {
  635.                 nextValue = (TOCValuePtr)cmGetNextListCell(theValue);
  636.                 CMfree(theValue);
  637.                 theValue = nextValue;
  638.             } /* value */
  639.             CMfree(theValueHdr);
  640.             theValueHdr = nextValueHdr;
  641.         } /* valueHdr */
  642.     } /* theDynProperty */
  643. }
  644.  
  645.  
  646. /*----------------------------------------------*
  647.  | cmFreeTOC - free all the structures in a TOC |
  648.  *----------------------------------------------*
  649.  
  650.  This routine is called to free all the structures in a TOC. For updating, a container may
  651.  have its own TOC and the target TOC.  Thus the TOC pointer is an explicit parameter. It
  652.  is passed as a pointer to the toc pointer because freeing the TOC will NULL out the
  653.  caller's pointer.
  654.  
  655.  All memory for objects, properties, and types is freed.  On return the toc pointer in the
  656.  container is NULL as well as all the master link head/tail pointers.
  657. */
  658.  
  659. void cmFreeTOC(ContainerPtr container, void **toc)
  660. {
  661.     TOCValueHdrPtr theValueHdr, nextValueHdr;
  662.     TOCValuePtr         theValue, nextValue;
  663.     
  664.     /* Free the main data structures...                                                                                                        */
  665.     
  666.     cmDestroyTOC(toc, (void *)container, true, cmFreeProperties);
  667.     
  668.     /* Free all the value headers on the deletedValues list. This list results in deletes    */
  669.     /* of objects, properties, or value headers themselves.  Objects are similarly put        */
  670.     /* on a separate list of deleted objects.  This list is freed by cmDestroyTOC().            */
  671.     
  672.     theValueHdr = (TOCValueHdrPtr)cmGetListHead(&container->deletedValues);
  673.     while (theValueHdr) {
  674.         nextValueHdr = (TOCValueHdrPtr)cmGetNextListCell(theValueHdr);
  675.         theValue = (TOCValuePtr)cmGetListHead(&theValueHdr->valueList);
  676.         while (theValue) {
  677.             nextValue = (TOCValuePtr)cmGetNextListCell(theValue);
  678.             CMfree(theValue);
  679.             theValue = nextValue;
  680.         } /* value */
  681.         CMfree(theValueHdr);
  682.         theValueHdr = nextValueHdr;
  683.     } /* valueHdr */
  684.     
  685.     cmInitList(&container->deletedValues);
  686. }
  687.  
  688.  
  689. /*-------------------------------*
  690.  | cmSetValueBytes - set a value |
  691.  *-------------------------------*
  692.  
  693.  This routine is used to just set the value (theValueBytes) with the specified data. The
  694.  data is placed in theValueBytes as a function of the specified type, dataType.  There
  695.  are 7 types:
  696.     
  697.  (1). Value_NotImm                This is the "normal" case for a non-immediate value data (a
  698.                             container offset) and data length.  The value/valueLen (i.e.,
  699.                                                     notImm) variant of a TOCValueBytes is used.
  700.                                                     
  701.  (2). Value_Imm_Chars        All of these use the corresponding imm variant of the ptr to the
  702.  (3). Value_Imm_Long         TOCValueBytes struct passed here.  The data is placed in the
  703.  (4). Value_Imm_Short        field according to type and, for the hell of it, the notImm
  704.  (5). Value_Imm_Byte          valueLen field set accordingly.  Note signed and unsigned are
  705.  (6). Value_Imm_Ptr          treated alike.
  706.                                                     
  707.  (7). Value_GlobalName        This is a special case for global name strings. The data is
  708.                                                     assumed to be a pointer to a global name string.  A global name
  709.                                                     symbol table entry is created (using cmDefineGlobalName()) with
  710.                                                     the string.  The pointer to the symbol table entry is set as
  711.                                                     the value data. Note, a global name symbol table entry also
  712.                                                     has a back pointer to its "owning" TOCValue. THE CALLER MUST
  713.                                                     SET THIS FIELD!  Here we only deal with the value bytes.
  714.                                                     
  715.  The function returns the input theValueBytes pointer as its result.  NULL is returned
  716.  for Value_GlobalName if the allocation fails and an error will have been reported (note,
  717.  as usual we're covering our ass again here).
  718. */
  719.  
  720. TOCValueBytesPtr cmSetValueBytes(const ContainerPtr container,
  721.                                                                  TOCValueBytesPtr theValueBytes,
  722.                                                                   ConstValueType dataType, CM_ULONG data,
  723.                                                                   CM_ULONG dataLength)
  724. {
  725.     GlobalNamePtr g;
  726.     CMBoolean            dup;
  727.     
  728.     switch (dataType) {                /* set 'em...                                                                                                */
  729.         case Value_NotImm:            theValueBytes->notImm.value         = (CM_ULONG)data;
  730.                                                         theValueBytes->notImm.valueLen = (CM_ULONG)dataLength;
  731.                                                         break;
  732.         
  733.         case Value_Imm_Chars:        if (dataLength > sizeof(CM_ULONG)) dataLength = sizeof(CM_ULONG);
  734.                                                         theValueBytes->imm.ulongValue  = 0;            /* zero extra bytes */
  735.                                                         memcpy((CM_CHAR *)theValueBytes->imm.ucharsValue, (CM_CHAR *)data, (size_t)dataLength);
  736.                                                         theValueBytes->notImm.valueLen = dataLength;
  737.                                                         break;
  738.         
  739.         case Value_Imm_Long:        theValueBytes->imm.ulongValue  = (CM_ULONG)data;
  740.                                                         theValueBytes->notImm.valueLen = (CM_ULONG)sizeof(CM_ULONG);
  741.                                                         break;
  742.         
  743.         case Value_Imm_Short:        theValueBytes->imm.ulongValue  = 0;            /* zero extra bytes */
  744.                                                         theValueBytes->imm.ushortValue = (CM_USHORT)data;
  745.                                                         theValueBytes->notImm.valueLen = (CM_ULONG)sizeof(CM_USHORT);
  746.                                                         break;
  747.         
  748.         case Value_Imm_Byte:        theValueBytes->imm.ulongValue  = 0;            /* zero extra bytes */
  749.                                                         theValueBytes->imm.ubyteValue  = (CM_UCHAR)data;
  750.                                                         theValueBytes->notImm.valueLen = (CM_ULONG)sizeof(CM_UCHAR);
  751.                                                         break;
  752.                                                         
  753.         case Value_GlobalName:    g = cmCreateGlobalName(container->globalNameTable, (CM_UCHAR *)data, &dup);
  754.                                                         if (g == NULL) {
  755.                                                             Container_Disable(container);
  756.                                                             ERROR2(CM_err_NoGlobalName, data, CONTAINERNAME);
  757.                                                             theValueBytes = NULL;
  758.                                                         } else if (dup) {
  759.                                                             Container_Disable(container);
  760.                                                             ERROR2(CM_err_DupGlobalName, data, CONTAINERNAME);
  761.                                                             theValueBytes = NULL;
  762.                                                         } else {
  763.                                                             theValueBytes->globalName.globalNameSymbol = g;
  764.                                                             theValueBytes->globalName.offset                      = 0;
  765.                                                         }
  766.                                                         break;
  767.     } /* switch */
  768.     
  769.     return (theValueBytes);
  770. }
  771.  
  772.  
  773. /*------------------------------------------------------------------------*
  774.  | cmSetTOCValueHdrFlags - echo a value's flags in its value header flags |
  775.  *------------------------------------------------------------------------*
  776.  
  777.  This routine is used to echo the settings of a value's flags in the value header which
  778.  "owns" the value (TOCValue's are on a list whose header is in a TOCValueHdr).  This is
  779.  done because a CMValue "refNum" that an API user is given and in turn given back to us
  780.  is a pointer to a TOCValueHdr.  It is sometimes more convenient therefore to check the
  781.  kind of value we have by looking at the header then "going out" to the value. In all but
  782.  continued values there is only one TOCValue entry on the valueList anyway.  So echoing
  783.  is more efficient than always going after the tail or head (they're the same) of a
  784.  valueList just to see the flags and the kind of value.    
  785.  
  786.  The function updates the valueFlags field in the passed TOCValueHdr according to the
  787.  value flags also passed.  The updated valueFlags are also returned as the function
  788.  result.
  789. */
  790.  
  791. CM_USHORT cmSetTOCValueHdrFlags(TOCValueHdrPtr theValueHdr, const CM_USHORT flags)
  792. {
  793.     CM_USHORT             valueFlags = (CM_USHORT)(theValueHdr->valueFlags & ValueCstFlags);
  794.     ContainerPtr     container;
  795.     TOCValuePtr         the1stValue;
  796.     CM_CHAR                 flagsStr[15];
  797.     
  798.     /* The only non-obvious thing here is that we must take special precautions for             */
  799.     /* continued values.  Since the last continued value flags does NOT say kCMContinued,    */
  800.     /* we check the first value of the given value header which must say it is continued.    */
  801.     /* From that we can reliably set the value header flag to say its value list is             */
  802.     /* continued.                                                                                                                                                    */
  803.     
  804.     if (flags & kCMGlobalName)                                                /* global names                                            */
  805.         valueFlags |= (ValueGlobal | ValueDefined);
  806.     else if (flags & kCMImmediate)                                        /* immediates                                                */
  807.         if (flags & kCMContinued) 
  808.             valueFlags |= (ValueImmediate | ValueContinued);
  809.         else
  810.             valueFlags |= (ValueImmediate | ValueDefined);
  811.     else if (flags & kCMContinued)                                         /* continued                                                */
  812.         valueFlags |= ValueContinued;
  813.     else if (flags & kCMDynValue)
  814.         valueFlags |= ValueDynamic;
  815.     else if (flags == 0)                                                             /* garden-variety value (...maybe)    */
  816.         if ((the1stValue = (TOCValuePtr)cmGetListHead(&theValueHdr->valueList)) != NULL &&
  817.                 (the1stValue->flags & kCMContinued) != 0)
  818.             valueFlags |= (ValueContinued | ValueDefined);
  819.         else
  820.             valueFlags |= ValueDefined;
  821.     else {                                                                                        /* huh?                                                            */
  822.         container = theValueHdr->container;
  823.         Container_Disable(container);
  824.         ERROR1(CM_err_Internal1, cmltostr(flags, -4, true, flagsStr));
  825.         valueFlags = theValueHdr->valueFlags;
  826.     }
  827.     
  828.     return (theValueHdr->valueFlags = valueFlags);
  829. }
  830.  
  831.  
  832. /*---------------------------------------------------*
  833.  | cmCreateValueSegment - create a new value segment |
  834.  *---------------------------------------------------*
  835.  
  836.  This routine is called to create a new value segment and to fill in its fields with the
  837.  passed parameters.  A pointer to the newly created value segment is returned.  NULL is
  838.  returned if there is an allocation error.
  839.  
  840.  Segments are created generally created for continued values and appended on to the value
  841.  chain whose header is container in a value header.  However, due to such things like
  842.  value inserts a value segment may not necessarily be appended to the end of its chain.
  843.  Thus this routine, which brings a value segment into existence, is a separate routine.
  844.  
  845.  Note, only the segment is created here.  It is up to the caller to chain it to its value
  846.  header and echo the flags in that header.
  847. */
  848.  
  849. TOCValuePtr cmCreateValueSegment(TOCValueHdrPtr theValueHdr, TOCValueBytesPtr value,
  850.                                                                    const CM_USHORT flags)
  851. {
  852.     ContainerPtr container = theValueHdr->container;
  853.     TOCValuePtr  theValue;
  854.  
  855.     if ((theValue = (TOCValuePtr)CMmalloc(sizeof(TOCValue))) == NULL) { /* create seg...    */
  856.         Container_Disable(container);
  857.         ERROR1(CM_err_NoValueEntry, CONTAINERNAME);
  858.         return (NULL);
  859.     }
  860.     
  861.     theValue->theValueHdr = theValueHdr;                                /* fill in the fields...                    */
  862. #if CMKEEP_CONTINUE_FLAG
  863.     theValue->flags                = flags;
  864. #else
  865.     theValue->flags                = flags & ~kCMContinued;
  866. #endif
  867.     theValue->value                = *value;
  868.     cmNullListLinks(theValue);
  869.     
  870.     theValue->container = container->updatingContainer;    /* who "owns" this new value            */
  871.     theValue->logicalOffset = 0;                                                /* filled in elsewhere                        */
  872.     
  873.     return (theValue);
  874. }
  875.  
  876.  
  877. /*----------------------------------------------------------*
  878.  | cmAppendValue - append a value segment to a value header |
  879.  *----------------------------------------------------------*
  880.  
  881.  This routine takes a pointer to a value definition (the actual bytes) and its flags
  882.  and creates a TOCValue struct which is appended to the specified value header.  The
  883.  function returns the value pointer as its result (i.e. pointer to the newly created
  884.  value).  NULL is returned and an error reported if the allocation of the TOCValue fails.
  885.  
  886.  This routine is used to append actual values to an existing value header.  It is allowed
  887.  to call cmDefineObject() with a NULL value bytes pointer.  In that case an object is 
  888.  created with a value header but no value sublist off of that header.  This routine can
  889.  be called to build that sublist.
  890.  
  891.  Internally, cmDefineObject() itself will result in a call to here to add the first value
  892.  if one is so passed to it.
  893. */
  894.                                                              
  895. TOCValuePtr cmAppendValue(TOCValueHdrPtr theValueHdr, TOCValueBytesPtr value,
  896.                                                     const CM_USHORT flags)
  897. {
  898.     ContainerPtr container = theValueHdr->container;
  899.     TOCValuePtr  theValue;
  900.     CMObjectID     propertyID;
  901.     CM_CHAR         offsetStr[15], lenStr[15];
  902.     
  903.     /* Check to see that the offset in the value is in range of the container. We only do    */
  904.     /* while we're loading a container (i.e., the container is not considered in a valid    */
  905.     /* state yet.                                                                                                                                                    */
  906.     
  907.     if (!container->tocFullyReadIn && (flags & kCMImmediate) == 0 && 
  908.             (value->notImm.value > container->maxValueOffset                     ||
  909.              value->notImm.value + value->notImm.valueLen > container->maxValueOffset)) {
  910.         Container_Disable(container);
  911.         ERROR3(CM_err_BadOffset, cmltostr(value->notImm.value, 1, false, offsetStr), 
  912.                                                          cmltostr(value->notImm.valueLen, 1, false, lenStr), CONTAINERNAME);
  913.         return (NULL);
  914.     }
  915.     
  916.     /* Create the value segment, fill in its fields, and add it to its value hdr list...    */
  917.  
  918.     theValue = cmCreateValueSegment(theValueHdr, value, flags);
  919.     if (theValue == NULL) return (NULL);
  920.     cmAppendListCell(&theValueHdr->valueList, theValue);
  921.     
  922.     /* By saving the current total value size in the value segment we get the logical         */
  923.     /* offset of this segment.  For only a single segment this will always be 0.  But for    */
  924.     /* continued values, each additional segment will then get its starting logical             */
  925.     /* offset.  By remembering the original offset of each segment we will be able to            */
  926.     /* "do the math" to determine inserts and deletes of this data should be edited (we        */
  927.     /* don't care about that here).                                                                                                                */
  928.     
  929.     theValue->logicalOffset = theValueHdr->size;                /* set segment's logical offset        */    
  930.     
  931.     /* Echo the flags and sum total size in the value header for this value entry...            */
  932.     
  933.     (void)cmSetTOCValueHdrFlags(theValueHdr, flags);        /* echo flags in the value hdr        */
  934.     
  935.     if ((theValueHdr->valueFlags & ValueGlobal) != 0)        /* global name                                        */
  936.         theValueHdr->size += GetGlobalNameLength(theValue->value.globalName.globalNameSymbol) + 1;
  937.     else if (theValue->flags & kCMImmediate)
  938.         theValueHdr->size += theValue->value.notImm.valueLen;    /* immediate byte, word, long */
  939.     else                                                                                        
  940.         theValueHdr->size += theValue->value.notImm.valueLen;    /* non-immediate data                    */
  941.     
  942.     /* If we're currently reading in the TOC (indicated by tocFullyReadIn == false), then    */
  943.     /* we must set the logical offset of each value segment.  This is the user's "view" of*/
  944.     /* the record.  We use it only for updating to determine insert/delete operations for    */
  945.     /* creating update information.                                                                                                                */
  946.     
  947.     /* Also during this time, we must build the global name table for values that                 */
  948.     /* correspond to global names.  This is an in-memory table of the global names to make*/
  949.     /* looking those things up more efficient.                                                                                        */
  950.     
  951.     if (!container->tocFullyReadIn)    {                                        /* set logicalSize while loading    */
  952.         theValueHdr->logicalSize = theValueHdr->size;            /*    (only used for updating)        */
  953.         
  954.         propertyID = theValueHdr->theProperty->propertyID;/* see if we have a global name...*/
  955.         if (propertyID == CM_StdObjID_GlobalTypeName ||
  956.                 propertyID == CM_StdObjID_GlobalPropName)
  957.             if (!cmBuildGlobalNameTable(theValue)) {                /* ...build (up) global name table*/
  958.                 CMfree(theValue);
  959.                 return (NULL);
  960.             }
  961.     }
  962.     
  963.     return (theValue);                                                                    /* give caller back the value ptr    */
  964. }
  965.  
  966.  
  967. /*--------------------------------------------*
  968.  | defineValue - define a value for an object |
  969.  *--------------------------------------------*
  970.  
  971.  This routine is used only by defineProperty() to create a value header and value for a
  972.  property (theProperty) associated with an object.  A value is chained to its value
  973.  header and the value headers to their property.  
  974.  
  975.  The value's typeID, value, generation and flags are given. The function returns a pointer
  976.  to the value's value header if the value was successfully created and NULL if it wasn't.
  977.  An error is reported if NULL is returned.  Note, while error calls are not supposed to
  978.  return we assume here they due just to be safe!
  979.  
  980.  The flags indicate the kind of value we have.  Continued values are special, in that any
  981.  continued value after the first for the current value header (the most recent one
  982.  created) do not cause a new value header to be created for the property.  The returned
  983.  value header is not linked into its property.  The caller, i.e., defineProperty() is
  984.  responsible for that.  It has to know whether a continued value caused a new value header
  985.  to be created.  Thus the newValueHdr switch is set true if it was and false if it is 
  986.  just another continued value.
  987.  
  988.  Note, the pointer to the value bytes may be NULL.  This indicates that only the value 
  989.  header for a yet to be defined value is to be created.
  990. */
  991.  
  992. static TOCValueHdrPtr CM_NEAR defineValue(ContainerPtr container,
  993.                                                                                     TOCPropertyPtr theProperty,
  994.                                                                                     const CM_ULONG typeID,
  995.                                                                                     TOCValueBytesPtr value,
  996.                                                                                     const CM_ULONG generation,
  997.                                                                                     const CM_USHORT flags,
  998.                                                                                     CMBoolean *newValueHdr)
  999. {
  1000.     TOCValueHdrPtr theValueHdr;
  1001.     
  1002.     /* If the last value for the property is for a continued value, then the new value is    */
  1003.     /* also considered continued (whether the flags say so or not -- the last continued        */
  1004.     /* value doesn't have its contined bit set).  Continued values must all have the same    */
  1005.     /* type ID.  Note, that since the last value of a set of continued set of values does    */
  1006.     /* not have the continued flag, but all of them cause the continued flag in the value    */
  1007.     /* header to be set, we need to know if the previous continued value was the last one.*/
  1008.     /* The ValueDefined bit in the header is used to do this (it's why it was created). If*/
  1009.     /* the last value was continued but is fully defined (because the last value was the    */
  1010.     /* end of the continuation -- its flags were 0), then we must have a NEW incoming         */
  1011.     /* value.    Having said all this, it should be noted that all this continue crap is         */
  1012.     /* used ONLY when reading in a TOC from the container.  At all other times continued    */
  1013.     /* values are "manufactured" directly by CMWriteValueData().                                                    */
  1014.     
  1015.     /* If we don't have a continued value, we create a new value header for the property.    */
  1016.     /* In all cases (continued and not continued) we, of course, create a new value entry.*/
  1017.     /* The only difference is weather the value header is a new one or a previous one.        */
  1018.     
  1019.     if ((theValueHdr = (TOCValueHdrPtr)cmGetListTail(&theProperty->valueHdrList)) != NULL &&
  1020.             (theValueHdr->valueFlags & ValueContinued) != 0 &&
  1021.             (theValueHdr->valueFlags & ValueDefined) == 0){    /* most recent value is cont'd...    */
  1022.         if (theValueHdr->typeID != typeID) {                            /* ...types must agree                        */
  1023.             Container_Disable(container);
  1024.             ERROR1(CM_err_BadContinue, CONTAINERNAME);
  1025.             return (NULL);
  1026.         }
  1027.         *newValueHdr = false;                                                            /* ...use most recent value hdr        */
  1028.     } else {                                                                                        /* create a new value header...        */
  1029.         if (container->tocFullyReadIn)                                        /* if valid TOC, check for dups...*/
  1030.             if (cmGetPropertyType(theProperty, typeID) != NULL) {
  1031.                 Container_Disable(container);
  1032.                 ERROR2(CM_err_DupType, cmGetGlobalTypeName(container, typeID), CONTAINERNAME);
  1033.                 return (NULL);
  1034.             }
  1035.             
  1036.         if ((theValueHdr = (TOCValueHdrPtr)CMmalloc(sizeof(TOCValueHdr))) == NULL) {
  1037.             Container_Disable(container);
  1038.             ERROR1(CM_err_NoValueEntry, CONTAINERNAME);
  1039.             return (NULL);
  1040.         }
  1041.  
  1042.         *newValueHdr = true;    
  1043.         cmNullListLinks(theValueHdr);                                            /* init value hdr fields...                */
  1044.         cmInitList(&theValueHdr->valueList);                            /* ...hdr is linked by caller            */
  1045.         theValueHdr->valueFlags    = 0;                                        /* ...no flags yet                                */
  1046.         theValueHdr->typeID             = typeID;                            /* ...use passed type ID                    */
  1047.         theValueHdr->container        = container;                        /* ...ptr to owning container            */
  1048.         theValueHdr->theProperty   = theProperty;                    /* ...ptr to owning property             */
  1049.         theValueHdr->size                   = 0;                                        /* ...total value size initially 0*/
  1050.         theValueHdr->logicalSize     = 0;                                        /* ...same for logicalSize                */
  1051.         theValueHdr->generation       = generation;                    /* ...generation number                        */
  1052.         theValueHdr->useCount           = 0;                                        /* ...no uses yet (well, not here)*/
  1053.         theValueHdr->valueRefCon   = NULL;                                /* ...no refCon value yet                    */
  1054.         RefDataObject(theValueHdr) = NULL;                                /* ...no value so no references        */
  1055.         theValueHdr->touch                  = NULL;                                /* ...not interested in touch here*/
  1056.         DYNEXTENSIONS(theValueHdr) = NULL;                                /* ...no dynamic value extensions    */
  1057.                                                                                                             
  1058.         /* Note: Dynamic value extensions are ONLY set by cmNewDynamicValue().                            */
  1059.         /*       References value extensions are ONLY set by ?().                                                        */
  1060.     }
  1061.     
  1062.     /* Now we can create a new value entry.  To make it easire to check for the various        */
  1063.     /* attributes of values we set the valueFlags in the value header according to the        */
  1064.     /* value flags themselves.  We only do it now if there currently is no value (see            */
  1065.     /* below for more about that). If there is a value, we will be calling cmAppendValue()*/
  1066.     /* which does the same thing.  There is no sense doing it twice.                                            */
  1067.     
  1068.     /* We also allow the pointer to the value bytes to be NULL.  This is defined as the     */
  1069.     /* creation of a object with a valueHdr but with NO VALUE.  This occurs when the API    */
  1070.     /* caller needs only a "refNum" to a value.  We use value header pointers as such            */
  1071.     /* "refNum"s.  Other API calls are done which specify the same object with a real         */
  1072.     /* value to be attached to the object. Values are then attached to the valueHdr, BUT    */
  1073.     /* NOT FROM HERE!  This routine is responsible only for creating objects WITH values.    */
  1074.     /* If the value creation is suppressed that is not this routines problem!  Take it        */
  1075.     /* somewhere else!                                                                                                                                        */
  1076.     
  1077.     if (value == NULL)                                                                    /* append new value if we have one*/
  1078.         (void)cmSetTOCValueHdrFlags(theValueHdr, flags);
  1079.     else if (cmAppendValue(theValueHdr, value, flags) == NULL)
  1080.         return (NULL);
  1081.     
  1082.     return (theValueHdr);                                                                /* give caller the value hdr ptr    */
  1083. }
  1084.  
  1085.  
  1086. /*----------------------------------------------------------------*
  1087.  | defineProperty - define a property and its value for an object |
  1088.  *----------------------------------------------------------------*
  1089.  
  1090.  This routine is used only by cmDefineObject() to create a property and its associated
  1091.  values for an object.  The properties are chained to the object, values to value headers,
  1092.  and the value headers to the properties.
  1093.  
  1094.  The value's typeID, value, generation and flags are given. The function returns a pointer
  1095.  to the property if the property was successfully created and NULL if it wasn't.  An error
  1096.  is reported if NULL is returned.  Note, while error calls are not supposed to return we
  1097.  assume here they due just to be safe!
  1098.  
  1099.  A new property for the object is only created if it has a different property id.  The
  1100.  value is chained to the property (a previous one or the new one for a new id). The
  1101.  returned property pointer is not linked to the object.  The caller, i.e., cmDefineObject()
  1102.  is responsible for that. It has to know whether a new property was created.  Thus the
  1103.  newProperty switch is set true if it was and false if it is just a previously used
  1104.  property for the object.
  1105.  
  1106.  Note, the value associated with the property is appended to the the end of the property's
  1107.  value list.  The caller has the option of getting the pointer to the value header 
  1108.  (theValueHdr) when theValueHdr is not passed as NULL.
  1109. */
  1110.  
  1111. static TOCPropertyPtr CM_NEAR defineProperty(ContainerPtr container,
  1112.                                                                                          TOCObjectPtr theObject,
  1113.                                                                                          const CM_ULONG propertyID,
  1114.                                                                                          const CM_ULONG typeID,
  1115.                                                                                          TOCValueBytesPtr value,
  1116.                                                                                          const CM_ULONG generation,
  1117.                                                                                          const CM_USHORT flags, CMBoolean *newProperty,
  1118.                                                                                          TOCValueHdrPtr *theValueHdr)
  1119. {
  1120.     TOCPropertyPtr theProperty;
  1121.     CMBoolean              newValueHdr;
  1122.     
  1123.     /* Scan all the properties for the object to see if we got a new one...                                */
  1124.     
  1125.     theProperty = cmGetObjectProperty(theObject, propertyID);
  1126.     *newProperty = (CMBoolean)(theProperty == NULL);
  1127.     
  1128.     /* If we got a new property create it and init its fields...                                                    */
  1129.     
  1130.     if (*newProperty) {                                                            /* new property...                                        */
  1131.         if ((theProperty = (TOCPropertyPtr)CMmalloc(sizeof(TOCProperty))) == NULL) {
  1132.             Container_Disable(container);
  1133.             ERROR1(CM_err_NoPropEntry, CONTAINERNAME);
  1134.             return (NULL);
  1135.         }
  1136.         cmNullListLinks(theProperty);
  1137.         cmInitList(&theProperty->valueHdrList);
  1138.         theProperty->propertyID = propertyID;
  1139.         theProperty->theObject  = theObject;                    /* note, ptr to owning object                    */
  1140.     }
  1141.     
  1142.     /* To make it easier to read this mess we define the value and its header separately!    */
  1143.     
  1144.     *theValueHdr = defineValue(container, theProperty, typeID, value, generation, flags, &newValueHdr);
  1145.     
  1146.     if (*theValueHdr == NULL) {
  1147.         if (*newProperty) CMfree(theProperty);
  1148.         return (NULL);
  1149.     }
  1150.     
  1151.     /* If we don't have a continued value, or even if we do, but its the first value of        */
  1152.     /* the continuation, add it to property's value list.                                                                 */
  1153.     
  1154.     if (newValueHdr)                                                                 /* if new value hdr...                                */
  1155.         cmAppendListCell(&theProperty->valueHdrList, *theValueHdr); /* ...add to end of list*/
  1156.         
  1157.     return (theProperty);                                                        /* give caller the property ptr                */
  1158. }
  1159.  
  1160.  
  1161. /*------------------------------------------------------------*
  1162.  | cmDefineObject - define a object with a property and value |
  1163.  *------------------------------------------------------------*
  1164.  
  1165.  This routine is called to define a new TOC entry for an object.  We may have either a
  1166.  new object (id) or a preexisting one for which a new property and value are to be 
  1167.  defined.  All the fields for a TOC entry are passed.  The objectFlags indicate the
  1168.  type of the object (more about this later).
  1169.  
  1170.  Note, the value associated with the property is appended to the the end of the
  1171.  property's value list.  The caller has the option of getting the pointer to the value
  1172.  header (theValueHdr) when theValueHdr is not passed as NULL.
  1173.  
  1174.  The function returns a pointer to the object if it was successfully created and NULL if
  1175.  it wasn't.  An error is reported if NULL is returned.  Note, while error calls are not
  1176.  supposed to return we assume here they due just to be safe!
  1177.  
  1178.  The objectFlags determine how we treat the object and all the object fields (the other
  1179.  parameters).  There are four possible objectFlags:
  1180.  
  1181.  (1). UndefinedObject            Set if the object is to be created, but we don't yet know what
  1182.                                                      its TOC entries are.  Basically a null object (or placeholder)
  1183.                                                     is created.  It is an incomplete object in that there are no
  1184.                                                     properties or types chained to the object (yet).  This flag may
  1185.                                                     be used in combination with the others if we know that the 
  1186.                                                     object ID corresponds to a property, type, or neither.
  1187.                                                     
  1188.  (2). ObjectObject                This flags is used when we don't know the type of the object but
  1189.                                                      we know a property and type for it.  If the object already exists
  1190.                                                     and it's undefined (UndefinedObject) it is now considered as
  1191.                                                     defined.  If it was an undefined property or type, it now becomes
  1192.                                                     a defined property or type.  Of course, duplicate definitions
  1193.                                                     are an error.
  1194.  
  1195.  (3). PropertyObject            This is similar to ObjectObject, but here we know the object is
  1196.                                                      for a property.  It has to either not exist previously, or was
  1197.                                                     previously flagged as UndefinedObject and PropertyObject.
  1198.  
  1199.  (4). TypeObject                    Same as PropertyObject, but for type objects.
  1200.  
  1201.  This routine is the "main control" of Container Manager object creation.  It is used
  1202.  for "normal" object creation and while loading in a TOC from a preexisting container
  1203.  (i.e., from cmReadTOC()). After cmReadTOC(), or if we are writing and never do a
  1204.  cmReadTOC() we have the "normal" case.  "We control the horizontal. Do not adjust your
  1205.  TV set". We always know what we are creating (yeah, I got a bridge to sell you too). 
  1206.  Thus, in that case, we can link the objects to their master lists. 
  1207.  
  1208.  For cmReadTOC() however, we are creating objects as we see thier id's in each TOC entry as
  1209.  we read them.  We can have forward references to undefined and yet to be fully
  1210.  created objects and backward references to existing objects.  We can also have multiple
  1211.  properties and values for the same object.  Hence the flags and all they imply.  We also
  1212.  do not link the objects.  That is delayed until the TOC is completely read in.  At that
  1213.  point the entire TOC is walked sequentially and the links built.  We can also do some
  1214.  additional validation to make sure that everything is defined after the read in.
  1215. */
  1216.  
  1217. TOCObjectPtr cmDefineObject(ContainerPtr container, CM_ULONG objectID,
  1218.                                                        const CM_ULONG propertyID, const CM_ULONG typeID, 
  1219.                                                          TOCValueBytesPtr value, const CM_ULONG generation,
  1220.                                                         const CM_USHORT flags, const CM_USHORT objectFlags,
  1221.                                                         TOCValueHdrPtr *theValueHdr)
  1222. {
  1223.     TOCObjectPtr      theObject;
  1224.     TOCPropertyPtr theProperty;
  1225.     TOCValueHdrPtr dummyValueHdr;
  1226.     CMBoolean             dup, newProperty;
  1227.     CM_CHAR                 idStr[15];
  1228.     
  1229.     /* If the object ID's wrap, the objectID value will go to 0x00000000.  We set it to        */
  1230.     /* this when we bump the ID counter and detect we will wrap if we should ever attempt    */
  1231.     /* to use another ID.  But we may not.  So the error is not reported when the wrap is    */
  1232.     /* detected.  The counter is only set to 0x00000000.  That ID will work its way into    */
  1233.     /* here to define another object.  Now we're attempting to use the ID.  So now is the    */
  1234.     /* time to report the error.                                                                                                                    */
  1235.     
  1236.     if (objectID == 0x00000000UL) {
  1237.         Container_Disable(container);
  1238.         ERROR1(CM_err_wrappedIDs, CONTAINERNAME);
  1239.         return (NULL);
  1240.     }
  1241.     
  1242.     /* Create the basic object.  If it already exists dup will be true  If it doesn't, it    */
  1243.     /* will be created.. Its fields will be initialized and the objectFlags set.                    */
  1244.     
  1245.     theObject = cmCreateObject(container->toc, (CMObjectID)objectID, objectFlags, &dup);
  1246.  
  1247.     if (theObject == NULL) {
  1248.         Container_Disable(container);
  1249.         ERROR1(CM_err_NoObjEntry, CONTAINERNAME);
  1250.         return (NULL);
  1251.     }
  1252.     
  1253.     /* If the object is to be an undefined place holder we ignore all the TOC field                */
  1254.     /* parameters.  But we must do a set of consistency checks before we will accept it.    */
  1255.     /* Here are the conditions which make an undefined object acceptable:                                    */
  1256.     
  1257.     /*        (1). The object is new (not a dup).  This is the first time we have seen it.        */
  1258.     
  1259.     /*        (2). The object has been seen before and it is still undefined.  Thus we are         */
  1260.     /*                 only trying to create the object because it was referenced more than once     */
  1261.     /*                  as we were loading a TOC.                                                                                                    */
  1262.     
  1263.     /*        (3). The object has been seen before and IS defined.  Its type is the same as        */
  1264.     /*                 what we are trying make a place holder for.  This occurs when the caller     */
  1265.     /*                  thinks the id may be undefined but knows it's a property or type.  If we        */
  1266.     /*                 already defined it it better be the same (i.e., property or type).                    */
  1267.     
  1268.     /*        (4). The object has been seen before and IS defined.  But it was flagged as an     */
  1269.     /*                 ObjectObject because we didn't know whether it was a property or type. The    */
  1270.     /*                 caller still thinks it may be undefined, but knows it's a property or type.*/
  1271.     /*                 We thus CHANGE the ObjectObject flag to property or type now that we know    */
  1272.     /*                  what it realy is. The end result is that a earlier defined but unknown kind*/
  1273.     /*                  of object (hence ObjectObject) now has a know kind, i.e., it's a property    */
  1274.     /*                 or type.  We have resolve a forward reference to an object.  Note that if    */
  1275.     /*               the forward reference was to a known kind (property or type) and the knew    */
  1276.     /*                 kind is different, we have an error. It is an attempt to multiply define     */
  1277.     /*                 the object as both a property and a type.                                                                    */
  1278.     
  1279.     /* In summary, whew...                                                                                                                                */
  1280.     
  1281.     if (objectFlags & UndefinedObject) {                        /* do what it says above!                            */
  1282.         if (!dup)                                                                         return (theObject);            /*    (1)         */
  1283.         if (theObject->objectFlags & UndefinedObject) return (theObject);            /*    (2)            */
  1284.         if (theObject->objectFlags & objectFlags)         return (theObject);            /*    (3)            */
  1285.         if (theObject->objectFlags & (PropertyObject | TypeObject)) {                    /*    (4)            */
  1286.             Container_Disable(container);
  1287.             ERROR2(CM_err_MultDef, cmltostr(objectID, 1, false, idStr), CONTAINERNAME);
  1288.             return (NULL);
  1289.         }
  1290.         theObject->objectFlags = (CM_USHORT)(objectFlags & ~UndefinedObject);
  1291.         return (theObject);                                                        /* give caller back the object                */
  1292.     }
  1293.     
  1294.     /* At this point we are trying to fully create an object.  We know what it is because    */
  1295.     /* the objectFlags didn't say UndefinedObject.  If the object already exists, but it  */
  1296.     /* is still undefined, we now mark it defined.                                                                                  */
  1297.     
  1298.     theObject->objectFlags &= ~UndefinedObject;
  1299.     
  1300.     /* If we get this far we are going to create a new property for an object or use an        */
  1301.     /* existing one if one for the same property ID already exists.  Either way the value    */
  1302.     /* will be added to the chain off the property.  The value will be appened to the end    */
  1303.     /* of the chain. Here it goes...                                                                                                            */
  1304.     
  1305.     if (theValueHdr == NULL) theValueHdr = &dummyValueHdr; /* supply dummy if necessary        */
  1306.         
  1307.     theProperty = defineProperty(container, theObject, propertyID, typeID, value, generation,
  1308.                                                              flags, &newProperty, theValueHdr);
  1309.     if (theProperty == NULL) return (NULL);
  1310.     
  1311.     /* If we indeed did create a new property add it to the object's property chain...        */
  1312.     
  1313.     if (newProperty) cmAppendListCell(&theObject->propertyList, theProperty);
  1314.     
  1315.     /* Finally, if we are working with a "valid" TOC (remember "we control the                         */
  1316.     /* horizontal") then link the object to the master lists.                                                            */
  1317.     
  1318.     if (container->tocFullyReadIn) cmLinkObject(container->toc, theObject, NULL);
  1319.     
  1320.     return (theObject);                                                            /* give object and valueHdr to caller    */
  1321. }
  1322.  
  1323.  
  1324. /*-----------------------------------------------------------------------------*
  1325.  | cmGetObjectProperty - find the property with the specified ID for an object |
  1326.  *-----------------------------------------------------------------------------*
  1327.  
  1328.  This routine takes a pointer to an object and scans its properties for the specified ID.
  1329.  The pointer to the property on the object's property chain is returned if found.  NULL is
  1330.  returned if not found.
  1331. */
  1332.  
  1333. TOCPropertyPtr cmGetObjectProperty(TOCObjectPtr theObject, CM_ULONG propertyID)
  1334. {
  1335.     TOCPropertyPtr theProperty;
  1336.         
  1337.     theProperty = (TOCPropertyPtr)cmGetListHead(&theObject->propertyList);
  1338.     
  1339.     while (theProperty) {                                                                    /* scan each property on list        */
  1340.         if (theProperty->propertyID == propertyID) break;        /* exit as soon as we find ID        */
  1341.         theProperty = (TOCPropertyPtr)cmGetNextListCell(theProperty);
  1342.     }
  1343.     
  1344.     return (theProperty);                                                                    /* return property ptr or NULL    */
  1345. }
  1346.  
  1347.  
  1348. /*---------------------------------------------------------------------------------------*
  1349.  | cmGetPropertyType - find the value (header) with the specified type ID for a property |
  1350.  *---------------------------------------------------------------------------------------*
  1351.  
  1352.  This routine takes a pointer to a object's property and scans its value headers for one
  1353.  containing the specified type ID.  The pointer to the value header is returned if found.
  1354.  NULL is returned if not found.
  1355. */
  1356.  
  1357. TOCValueHdrPtr cmGetPropertyType(TOCPropertyPtr theProperty, CM_ULONG typeID)
  1358. {
  1359.     TOCValueHdrPtr theValueHdr;
  1360.         
  1361.     theValueHdr = (TOCValueHdrPtr)cmGetListHead(&theProperty->valueHdrList);
  1362.     
  1363.     while (theValueHdr) {                                                                    /* scan each value hdr on list    */
  1364.         if (theValueHdr->typeID == typeID) break;                        /* exit as soon as we find ID        */
  1365.         theValueHdr = (TOCValueHdrPtr)cmGetNextListCell(theValueHdr);
  1366.     }
  1367.     
  1368.     return (theValueHdr);                                                                    /* return value hdr ptr or NULL    */
  1369. }
  1370.  
  1371.  
  1372. /*----------------------------------------------------------------------------*
  1373.  | cmGet1ValueSize - return the size of the data for a single value (segment) |
  1374.  *----------------------------------------------------------------------------*
  1375.  
  1376.  This routine takes a pointer to a value (NOT a value header -- continued values are not
  1377.  worried about here) and returns the size represented by that value.  For non-immediate
  1378.  values we simply return the length field from the value.  Immediate value sizes are
  1379.  dependent on their type.
  1380. */
  1381.  
  1382. CM_ULONG cmGet1ValueSize(TOCValuePtr theValue)
  1383. {
  1384.     CM_ULONG size;
  1385.     
  1386.     if (theValue->flags & kCMGlobalName)
  1387.         size = GetGlobalNameLength(theValue->value.globalName.globalNameSymbol) + 1;
  1388.     else if (theValue->flags & kCMImmediate)
  1389.         size = theValue->value.notImm.valueLen;                /* immediate byte, word, or long data    */
  1390.     else                                                                                        
  1391.         size = theValue->value.notImm.valueLen;                /* non-immediate data                                    */
  1392.         
  1393.     return (size);
  1394. }
  1395.  
  1396.  
  1397. #if CMVALIDATE
  1398. /*-------------------------------------------------------------------------------*
  1399.  | cmValidateCMValue - validate a CMValue "refNum" passed to the API by the user |
  1400.  *-------------------------------------------------------------------------------*
  1401.  
  1402.  CMValue "refNum"s in the API are implemented in the Container Manager as pointers to 
  1403.  TOCValueHdr structs.  This routine is used to filter "bad" value "refNum"s passed to the
  1404.  higher level API interface  ("CM...") routines.  False is returned if the value is
  1405.  considered as "bad" and true otherwise.
  1406.  
  1407.  As currently implemented we return false but do NOT report an error for NULL pointers.
  1408.  That is because we return NULL from everthing we error out of earlier.  The NULL test is
  1409.  just protection from letting such pointer from ever being used.
  1410.  
  1411.  Another check we do without reporting an error is on the container pointer.  The refNums
  1412.  have a pointer to their container.  The container has a special pointer pointing to 
  1413.  itself.  We check agains this pointer.  If we think we don't have a valid container
  1414.  pointer then we assume we have a bad refNum and return false.  No error can be reported
  1415.  because, wiut a container pointer, there is no way to do it!
  1416.  
  1417.  We DO report an error for attempting to use a deleted value.  Deleted values are NOT
  1418.  freed.  Rather they (actually their value headers) are put on a list of deleted values
  1419.  and flagged as deleted just for the purpose of testing here.  If we had freed them, then
  1420.  we would be getting pointers to "garbage". By keeping them around we have valid pointers
  1421.  and we can do the test. Yuk!
  1422. */
  1423.  
  1424. CMBoolean cmValidateCMValue(CMValue value)
  1425. {
  1426.     ContainerPtr container;
  1427.     
  1428.     if (value == NULL) return (false);                                /* false if bad value                                */
  1429.     
  1430.     container = ((TOCValueHdrPtr)value)->container;        /* get container from refNum                */
  1431.     
  1432.     NOPifNotInitialized(false);                                                /* false if not initialized (NULL)    */
  1433.  
  1434.     if (container != container->thisContainer)                /* check against the check ptr            */
  1435.         return (false);
  1436.     
  1437.     container = container->updatingContainer;                    /* use updating container                        */
  1438.     
  1439.     if (!VALIDATE) return (true);                                            /* dynamically suppressed checking    */
  1440.     
  1441.     /* Make sure value has not been deleted...                                                                                        */
  1442.     
  1443.     if ((((TOCValueHdrPtr)value)->valueFlags & ValueDeleted) != 0) {
  1444.         Container_Disable(container);
  1445.         ERROR1(CM_err_BadValue, CONTAINERNAME);
  1446.         return (false);
  1447.     }
  1448.     
  1449.     return (true);                                                                        /* ok, it's good -- maybe!                    */
  1450. }
  1451. #endif
  1452.  
  1453.  
  1454. #if CMVALIDATE
  1455. /*---------------------------------------------------------------------------------*
  1456.  | cmValidateCMObject - validate a CMObject "refNum" passed to the API by the user |
  1457.  *---------------------------------------------------------------------------------*
  1458.  
  1459.  CMObject (and CMProperty, and CMType) "refNum"s in the API are implemented in the
  1460.  Container Manager as pointers to TOCObject structs.  This routine is used to filter "bad"
  1461.  object "refNum"s  passed to the higher level API interface  ("CM...") routines.  False is
  1462.  returned if the object is considered as "bad" and true otherwise.
  1463.  
  1464.  As currently implemented we return false but do NOT report an error for NULL pointers.
  1465.  That is because we return NULL from everthing we error out of earlier.  The NULL test is
  1466.  just protection from letting such pointer from ever being used.
  1467.  
  1468.  Another check we do without reporting an error is on the container pointer.  The refNums
  1469.  have a pointer to their container.  The container has a special pointer pointing to 
  1470.  itself.  We check agains this pointer.  If we think we don't have a valid container
  1471.  pointer then we assume we have a bad refNum and return false.  No error can be reported
  1472.  because, wiut a container pointer, there is no way to do it!
  1473.  
  1474.  We DO report an error for attempting to use a deleted object.  Deleted objects are NOT
  1475.  freed.  Rather they are put on a list of deleted objects and flagged as deleted just for
  1476.  the purpose of testing here.  If we had freed them, then we would be getting pointers to
  1477.  "garbage".  By keeping them around we have valid pointers and we can do the test.
  1478.  
  1479.     For types and properties we also validate that such "refNum"s are indeed types or
  1480.     properties.  The desired type is passed in the objectFlags.  This test can always be
  1481.     forced to pass by passing 0xFFFFU.  For types and properties, TypeObject or 
  1482.     PropertyObject should be passed as appropriate.
  1483. */
  1484.  
  1485. CMBoolean cmValidateCMObjects(CMObject object, CM_USHORT objectFlags)
  1486. {
  1487.     ContainerPtr container;
  1488.     
  1489.     if (object == NULL) return (false);                                /* false if bad value                                */
  1490.     
  1491.     container = ((TOCObjectPtr)object)->container;        /* get container from refNum                */
  1492.     
  1493.     NOPifNotInitialized(false);                                                /* false if not initialized (NULL)    */
  1494.     
  1495.     if (container != container->thisContainer)                /* check against the check ptr            */
  1496.         return (false);
  1497.  
  1498.     container = container->updatingContainer;                    /* use updating container                        */
  1499.     
  1500.     if (!VALIDATE) return (true);                                            /* dynamically suppressed checking    */
  1501.     
  1502.     /* Note, we assume that if an object is not NULL, it does indeed point to a container    */
  1503.     /* object.  We must make that assumption because it's the only way we can get the         */
  1504.     /* container pointer.  We return NULLs from everwhere when something goes wrong, so     */
  1505.     /* the probability that a non-NULL is an object is pretty high.  Besides, if the user    */
  1506.     /* follos the rules, the second we report an error, the error report should abort            */
  1507.     /* execution!                                                                                                                                                    */
  1508.     
  1509.     /* Make sure object has not been deleted...                                                                                        */
  1510.  
  1511.     if ((((TOCObjectPtr)object)->objectFlags & DeletedObject) != 0) {
  1512.         Container_Disable(container);
  1513.         ERROR1(CM_err_BadObject, CONTAINERNAME);
  1514.         return (false);
  1515.     }
  1516.  
  1517.     /* Make sure the kind of object is what is desired...                                                                    */
  1518.  
  1519.     if ((((TOCObjectPtr)object)->objectFlags & objectFlags) == 0) {
  1520.         Container_Disable(container);
  1521.         ERROR2(CM_err_BadObjectKind, (objectFlags & TypeObject) ? "type" : "property", CONTAINERNAME);
  1522.         return (false);
  1523.     }
  1524.     
  1525.     return (true);                                                                        /* ok, it's good -- maybe!                    */
  1526. }
  1527. #endif
  1528.  
  1529.  
  1530. /*----------------------------------------------------------*
  1531.  | walkObject - walk all the structures for a single object |
  1532.  *----------------------------------------------------------*
  1533.  
  1534.  This routine serves two purposes. First it is an action routine for the object interator,
  1535.  cmForEachObject(). It is set by cmWalkThroughEntireTOC() to walk all the structures for a
  1536.  single object.  Thus the net result from cmWalkThroughEntireTOC()'s point of view is that
  1537.  each structure in the the TOC is visited.  Each object (by ascending order of object id),
  1538.  each property for each object, and each value (header and value) for each property.
  1539.  
  1540.  The second purpose of this routine is to act as the "guts" of cmWalkObject().  There are
  1541.  possible situations where we only want to walk a single object.  Since that's what this
  1542.  routine does, cmWalkObject() calls it explicitly instead of repeatedly from 
  1543.  cmForEachObject().  The result is the same.  One object is walk per call.
  1544.  
  1545.  At each point in the data structures linked to an object we call a action routine
  1546.  specific to that structure.  There is a object action routine (objectAction), a property
  1547.  action routine (propertyAction) and a value action routine (valueAction).  See
  1548.  cmWalkThroughEntireTOC() or cmWalkObject() for further details on these routines.  Note
  1549.  that if a routine pointer is NULL, no action is taken for that structure.  There are also
  1550.  conventions for skipping the walking of parts of the data structures.  Again, see
  1551.  cmWalkThroughEntireTOC() for details.
  1552.  
  1553.  The refcon in this context is the container pointer.  It, in turn, contains a pointer
  1554.  back to a structure defined locally in cmWalkThroughEntireTOC() or cmWalkObject()
  1555.  containing all the action routine pointers and the original caller's refCon.  See
  1556.  cmWalkThroughEntireTOC() or cmWalkObject() for details.
  1557.  
  1558.  Note, this "static" is intentionally left to default memory model under DOS since it is
  1559.  passed as a function pointer to cmForEachObject().
  1560. */
  1561.  
  1562. static void walkObject(TOCObjectPtr theObject, CMRefCon refCon)
  1563. {
  1564.     ContainerPtr      container  = (ContainerPtr)refCon;
  1565.     TOCActionsPtr  tocActions = (TOCActionsPtr)container->tocActions;
  1566.     TOCPropertyPtr theProperty, nextProperty;
  1567.     TOCValueHdrPtr theValueHdr, nextValueHdr;
  1568.     TOCValuePtr         theValue, nextValue;
  1569.     TOCWalkReturns action;
  1570.     
  1571.     /* From this point on, the refCon to the action routines will be whatever the caller    */
  1572.     /* supplied to cmWalkThroughEntireTOC(). We overload our used of "refCon" since                */
  1573.     /* there's  no sense creating another variable.                                                                                */
  1574.     
  1575.     refCon = tocActions->refCon;
  1576.     
  1577.     /* Call the action routine to handle object structures...                                                            */
  1578.     
  1579.     if (tocActions->objectAction) 
  1580.         if ((*tocActions->objectAction)(container, theObject, refCon) == WalkNextTOCObject)
  1581.             return;
  1582.     
  1583.     /* If there are no property, valueHdr, or value actions, there is nothing more to do..*/
  1584.     
  1585.     if (tocActions->propertyAction == NULL &&
  1586.             tocActions->valueHdrAction == NULL &&
  1587.           tocActions->valueAction == NULL) 
  1588.         return;
  1589.     
  1590.     /* Walk through the property list for this object, calling its action routine...            */
  1591.     
  1592.     theProperty = (TOCPropertyPtr)cmGetListHead(&theObject->propertyList);
  1593.     while (theProperty) {
  1594.         nextProperty = (TOCPropertyPtr)cmGetNextListCell(theProperty);
  1595.         
  1596.         if (tocActions->propertyAction) {
  1597.             action = (*tocActions->propertyAction)(container, theProperty, refCon);
  1598.             if (action == WalkNextTOCProperty) goto nextproperty;
  1599.             if (action == WalkNextTOCObject)      return;
  1600.         }
  1601.         
  1602.         /* Walk through the value header list for each property, calling the action routine    */
  1603.         /* for them.  Note the value header pointer is passed since a single value is                */
  1604.         /* considered as all the value entries chained off a single value header.  That            */
  1605.         /* only comes about from continued values.                                                                                    */
  1606.         
  1607.         if (tocActions->valueHdrAction || tocActions->valueAction) {
  1608.             theValueHdr = (TOCValueHdrPtr)cmGetListHead(&theProperty->valueHdrList);
  1609.             while (theValueHdr) {
  1610.                 nextValueHdr = (TOCValueHdrPtr)cmGetNextListCell(theValueHdr);
  1611.                 
  1612.                 if (tocActions->valueHdrAction) {
  1613.                     action = (*tocActions->valueHdrAction)(container, theValueHdr, refCon);
  1614.                     if (action == WalkNextTOCValueHdr) goto nextvaluehdr;
  1615.                     if (action == WalkNextTOCProperty) goto nextproperty;
  1616.                     if (action == WalkNextTOCObject)   return;
  1617.                 }
  1618.                 
  1619.                 if (tocActions->valueAction) {
  1620.                     theValue = (TOCValuePtr)cmGetListHead(&theValueHdr->valueList);
  1621.                     while (theValue) {
  1622.                         nextValue = (TOCValuePtr)cmGetNextListCell(theValue);
  1623.                         action = (*tocActions->valueAction)(container, theValue, refCon);
  1624.                         theValue = nextValue;
  1625.                         if (action == WalkNextTOCValueHdr) goto nextvaluehdr;
  1626.                         if (action == WalkNextTOCProperty) goto nextproperty;
  1627.                         if (action == WalkNextTOCObject)   return;
  1628.                     } /* value */
  1629.                 }
  1630.                 
  1631.                 nextvaluehdr:
  1632.                 theValueHdr = nextValueHdr;
  1633.             } /* valueHdr */
  1634.         }
  1635.         
  1636.         nextproperty:        
  1637.         theProperty = nextProperty;
  1638.     } /* property */
  1639. }
  1640.                     
  1641.  
  1642. /*-------------------------------------------------------------------*
  1643.  | cmWalkThroughEntireTOC - apply actions to each structure in a TOC |
  1644.  *-------------------------------------------------------------------*
  1645.  
  1646.  This routine is called to walk the entire specified TOC by ascending object ID, starting 
  1647.  with object ID's greater than or equal to the startingID and less than or equal to
  1648.  endingID, and to call an action routine appropriate for each kind of structure: object,
  1649.  property, value header, and value.  
  1650.  
  1651.  Pointers to the four action routines are passed with headers as shown in the definition. 
  1652.  If a pointer is passed as NULL, no action will be performed for the corresponding
  1653.  structure.  Each function has return codes (type TOCWalkReturns) that direct the walking
  1654.  through the TOC.    The action routines should return the codes shown in the following
  1655.  table:
  1656.             
  1657.                       Walk...  NextTOCObject | NextTOCProperty | NextTOCValueHdr | NextTOCValue
  1658.         ===============|===============|=================|=================|==============
  1659.         objectAction   |  next object  |  next property* |                 |
  1660.         propertyAction |  next object  |  next property  | next value hdr* |
  1661.         valueHdrAction |  next object  |  next property  | next value hdr  | next segment*
  1662.         valueAction    |  next object  |  next property  | next value hdr  | next segment*
  1663.         ---------------+---------------+-----------------+-----------------+--------------
  1664.  
  1665.  The entries marked with "*" are the returns that should be used in the normal case to
  1666.  fully walk each data structure of the TOC.  Those not marked with "*" are for special
  1667.  cases to abort the walk to go to the next indicated data structure.  Walks are sequential
  1668.  through the TOC by ascending object ID.  Thus skipping a particular walk of one structure
  1669.  only makes sense if the skip is possible.  That is the reason not all returns are allowed
  1670.  in all action routines.  In reality, only the non-starred returns are checked on return
  1671.  from an action routine. So the default is the starred return (i.e., keep walking normally)
  1672.  if a non-starred is not returned.
  1673.  
  1674.  A "refCon" is also passed which the caller can use as a communication facility to convey
  1675.  additional info to the action routines.  
  1676.  
  1677.  This function returns 0 to indicate successful completion, and non-zero otherwise.  Thus
  1678.  if AbortWalkThroughEntireTOC(x) is used to abort processing, the x should be a positive
  1679.  non-zero integer.
  1680.  
  1681.  Note, if the property, value header, and value action routines are supplied as NULL,
  1682.  then cmWalkThroughEntireTOC() is functionally equivalent to cmForEachObject() which just
  1683.  applies a single action routine to the objects.  Thus if only the objects are to be
  1684.  walked, it is recommended that cmForEachObject() be used instead of
  1685.  cmWalkThroughEntireTOC().
  1686.  
  1687.  Note also, this routine uses cmForEachObject() to walk the TOC objects with walkObject()
  1688.  defined above as the action routine.  It handles the walking of the structures "below"
  1689.  the object level. We use the refCon parameter to cmForEachObject() to communicate to
  1690.  walkObject() all the action routine pointers and the caller's refCon.  In particular,
  1691.  the refCon is a pointer to the container.  In the container is a pointer BACK to a local
  1692.  data structure we set up here with all the info.
  1693.  
  1694.  We can get away with this stunt because this routine is always up level in the stack and
  1695.  its locals are valid while cmForEachObject() is doing its thing.
  1696.  
  1697.  The additional trick we pull here is to set up a setjmp/longjmp environment vector for
  1698.  the AbortWalkThroughEntireTOC() macro.  That macro is a longjmp back to here with a
  1699.  value we return as the function result.  Since the data structure is local here, the only
  1700.  way the macro can get at the environment vector is through the pointer in the container.
  1701.  I don't want the local data structure visable outside this file.  So the only place the
  1702.  vector can be placed is at the START of the data structure.  Win a few, loose a few!
  1703. */
  1704.  
  1705. int cmWalkThroughEntireTOC(ContainerPtr container, void *toc, 
  1706.                                                      CMObjectID startingID, CMObjectID endingID,
  1707.                                                      CMRefCon refCon,
  1708.                                                      TOCWalkReturns (*objectAction)(ContainerPtr container, TOCObjectPtr theObject, CMRefCon refCon),
  1709.                                                      TOCWalkReturns (*propertyAction)(ContainerPtr container, TOCPropertyPtr theProperty, CMRefCon refCon),
  1710.                                                      TOCWalkReturns (*valueHdrAction)(ContainerPtr container, TOCValueHdrPtr theValueHdr, CMRefCon refCon),
  1711.                                                      TOCWalkReturns (*valueAction)(ContainerPtr container, TOCValuePtr theValue, CMRefCon refCon))
  1712. {    
  1713.     TOCActions tocActions;
  1714.     
  1715.     tocActions.objectAction        = objectAction;                    /* save the action routine ptrs            */
  1716.     tocActions.propertyAction    = propertyAction;
  1717.     tocActions.valueHdrAction = valueHdrAction;
  1718.     tocActions.valueAction        = valueAction;
  1719.     
  1720.     tocActions.refCon                    = refCon;                                /* save caller's refCon                            */
  1721.     
  1722.     if (setjmp(tocActions.walkThroughEnv))                        /* if longjmp taken...                            */
  1723.         return (1);                                                                            /* ...report the bad news                        */
  1724.     
  1725.     container->tocActions = (void *)&tocActions;            /* set ptr to our info in container    */
  1726.  
  1727.     return (cmForEachObject(toc, startingID, endingID, (void *)container, walkObject));
  1728. }
  1729.  
  1730.  
  1731. /*-----------------------------------------------------------------------*
  1732.  | cmWalkObject - apply actions to each structure of a single TOC object |
  1733.  *-----------------------------------------------------------------------*
  1734.  
  1735.  This routine is called to walk a single object (theObject) and call an action routine
  1736.  appropriate for each kind of structure: object, property, value header, and value. 
  1737.  Pointers to the four action routines are passed with headers as shown in the definition.
  1738.  If a pointer is passed as NULL, no action will be performed for the corresponding
  1739.  structure.  The returns are the same as cmWalkThroughEntireTOC().  See it for further
  1740.  details.
  1741.  
  1742.  A "refCon" is also passed which the caller can use as a communication facility
  1743.  to convey additional info to the action routines.  0 is returned to indicate successfull
  1744.  completion.  Use the AbortWalkObject(x) (a macro) in an action routine to abort the
  1745.  walk.  The "x" is a integer which is returned from cmWalkObject() so it should not be 0.
  1746.  
  1747.  Implementation note: This routine is basically "glue" code for outside callers to 
  1748.  walkObject().  walkObject() is defined as an action routine for cmForEachObject() which is
  1749.  called by cmWalkThroughEntireTOC() above.  The walkObject() routine wals a single object
  1750.  and applies the action routines at each point in an objects data structure.  This is
  1751.  exactly what is needed here so that why we use.  There's no sence reinventing the wheel!
  1752.  
  1753.  Of course, since walkObject() doesn't know we are calling it from here, and things it's an
  1754.  action routine for cmForEachObject(), which was called by cmWalkThroughEntireTOC(), then
  1755.  we must observe ALL the conventions set up by cmWalkThroughEntireTOC().  See comments
  1756.  for cmWalkThroughEntireTOC() for these conventsions (the refCon, aborting, etc.).
  1757.  
  1758.  The only thing different in the code below is that we explicitly call walkObject() instead
  1759.  of cmForEachObject() and a more apporpriate "abort" macro is provided.  The macro is
  1760.  called AbortWalkObject(). But expands to identically to what AbortWalkThroughEntireTOC()
  1761.  expands to.
  1762. */
  1763.  
  1764. int cmWalkObject(ContainerPtr container, TOCObjectPtr theObject, CMRefCon refCon,
  1765.                                  TOCWalkReturns (*objectAction)(ContainerPtr container, TOCObjectPtr theObject, CMRefCon refCon),
  1766.                                  TOCWalkReturns (*propertyAction)(ContainerPtr container, TOCPropertyPtr theProperty, CMRefCon refCon),
  1767.                                  TOCWalkReturns (*valueHdrAction)(ContainerPtr container, TOCValueHdrPtr theValueHdr, CMRefCon refCon),
  1768.                                  TOCWalkReturns (*valueAction)(ContainerPtr container, TOCValuePtr theValue, CMRefCon refCon))
  1769. {
  1770.     TOCActions tocActions;
  1771.     
  1772.     tocActions.objectAction        = objectAction;                    /* save the action routine ptrs            */
  1773.     tocActions.propertyAction    = propertyAction;
  1774.     tocActions.valueHdrAction = valueHdrAction;
  1775.     tocActions.valueAction        = valueAction;
  1776.     
  1777.     tocActions.refCon                    = refCon;                                /* save caller's refCon                            */
  1778.     
  1779.     if (setjmp(tocActions.walkThroughEnv))                        /* if longjmp taken...                            */
  1780.         return (1);                                                                            /* ...report the bad news                        */
  1781.         
  1782.     container->tocActions = (void *)&tocActions;            /* set ptr to our info in container    */
  1783.  
  1784.     walkObject(theObject, (void *)container);                    /* walk single object    (not a beast)    */
  1785.     
  1786.     return (0);                                                                                /* what do you know, we made it!        */
  1787. }
  1788.                                                          
  1789.                                                           CM_END_CFUNCTIONS
  1790.